package com.example.musicproject.ThreadScheduleTask;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.pojo.collection;
import com.example.common.pojo.musicarticle;
import com.example.service.MusicArticleservice;
import com.example.service.MusicCollectservice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@Component
@EnableAsync  //开启多线程
@EnableScheduling   //开启定时任务
public class MultithreadScheduleTask {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MusicCollectservice musicCollectservice;

    @Autowired
    private MusicArticleservice musicArticleservice;
    /*
    *
    * 对于文章和歌曲id点赞的存储，
    *   在reids存储文章id是前面加一个前缀，也存在userid的set集合中，
    *   取出时，做一个判断前缀，然后插入数据库
    *
    * 点赞的定时任务,
    *   1.批量获取redis中的数据
    *   2.判断表中是否存在相同的值
    *       2.1将不存在的都列出来，再在批量插入
    * 重点：
    * 点赞收藏功能：
    *   1.在redis中加入点赞的文章id和作者id
    *       1.1可以进行点赞，点赞在redis中添加，然后定时将redis中数据添加到数据库中，然后删除redis中缓存的数据
    *   2.更改redis中prefect数据+1
    * 取消点赞功能：
    *   1.在redis中查询是否存在取消点赞的文章id和作者id
    *       1.1如何存在将其删除
    *       1.2如果不存在，再到数据库中查看数据是否存在，如果存在将其删除
    *   2.更改redis中prefect数据-1
    * 查看点赞的文章：
    *   将根据作者id或文章id将数据库中的记录和redis中的记录相加
    *   项目结束时，将redis数据载入数据库中
    *
    * 是否点赞：
    *   直接请求，在redis中查询看是否存在点赞，如果存在点赞则返回，如果不存在，再在数据库中查是否存在
    *
    * 是否点赞和浏览数
    *   使用zset数据格式，将文章的id和prefect和view_counts字段，载入redis中
    *   如何对redis进行加减，
    *   设置定时任务，将数据同步更新到数据库中
    *   项目结束时，将剩余的数据加载到redis中
    *   项目启动时，将数据载入redis
    *
    * */

    @PostConstruct
    public void reload(){
        Boolean isperfect = true;
        Boolean perfect = true;
        Boolean view = true;
        LambdaQueryWrapper<musicarticle> wapper = new LambdaQueryWrapper<>();
        wapper.select(musicarticle::getId,musicarticle::getIsperfect,musicarticle::getPerfect,musicarticle::getViewCounts);
        List<musicarticle> list = musicArticleservice.list(wapper);
        for (musicarticle m: list
             ) {
            isperfect = redisTemplate.boundZSetOps("isperfect").add(String.valueOf(m.getId()), m.getIsperfect());
            perfect = redisTemplate.boundZSetOps("perfect").add(String.valueOf(m.getId()), m.getPerfect());
            view = redisTemplate.boundZSetOps("view").add(String.valueOf(m.getId()), m.getViewCounts());
        }
        try {
            if(isperfect || perfect || view){
                log.info("项目启动，mysql转redis成功!");
            }
        }catch (Exception e){
            log.info(String.valueOf(e));
        }

    }

    @Async
//  “30 * * * * ?” 30秒执行一次 "0 0 12 * * ?" 每天中午12点触发
    @Scheduled(cron = "0 0 12 * * ?")
    public void collectionSchedule(){
        long startTime = System.currentTimeMillis();
//        HashMap<Long, Long> collectmap = new HashMap<>();
        LambdaQueryWrapper<collection> wapper = new LambdaQueryWrapper<>();
        try {
            boolean b = musicCollectservice.saveBatch(autoredistomysql());
            if (b){
                long endTime = System.currentTimeMillis();
                System.out.println("函数执行时间：" + (endTime - startTime) + "ms");
            }
        } catch (Exception e) {
            //处理异常
        }
    }

//    点赞浏览是否点赞数记录
    @Async
//  “30 * * * * ?” 30秒执行一次 "0 0 12 * * ?" 每天中午12点触发
    @Scheduled(cron = "0 0 12 * * ?")
    public void coutSchedule(){
        autocount();
    }

    @PreDestroy
    public void redestroy(){

        try {
            boolean b = musicCollectservice.saveBatch(autoredistomysql());
            autocount();
            log.info("项目关闭！");
            Boolean perfect = redisTemplate.delete("perfect");
            Boolean isperfect = redisTemplate.delete("isperfect");
            Boolean view = redisTemplate.delete("view");
            if (b || perfect || isperfect ||view){
                log.info("项目关闭，数据加载成功！");
            }else {
                log.info("项目关闭，数据加载失败");
            }
        } catch (Exception e) {
            //处理异常
        }
    }

//    将redis中数据包装为arraylist，如何删除redis，如何返回
    public ArrayList<collection> autoredistomysql(){
        ArrayList<collection> collectionList =new ArrayList<>();
        Set<String> setuserid = redisTemplate.boundSetOps("setKey").members();
        for (String userid: setuserid
        ) {
            Set<String> setarticleid = redisTemplate.boundSetOps(userid).members();
            for (String articleid: setarticleid
            ) {
//                collectmap.put(Long.valueOf(userid),Long.valueOf(articleid));
                collection collection = new collection();
                collection.setUserId(Long.valueOf(userid));
                collection.setArticleId(Long.valueOf(articleid));
                collectionList.add(collection);
            }
            //TODO 删除点赞缓存
            Boolean result2 = redisTemplate.delete(userid);
            Long result1 = redisTemplate.boundSetOps("setKey").remove(userid);
        }
        return collectionList;
    }
//    点赞数，浏览数，是否收藏数
    public void autocount(){
        ArrayList<musicarticle> malist = new ArrayList<>();
        Set<String> range = redisTemplate.boundZSetOps("perfect").range(0, -1);
        for (String s:range
        ) {
            musicarticle mal = new musicarticle();
            Double perfectscore = redisTemplate.boundZSetOps("perfect").score(s);
            Double isperfectscore = redisTemplate.boundZSetOps("isperfect").score(s);
            Double viewscore = redisTemplate.boundZSetOps("view").score(s);
            mal.setId(Long.valueOf(s));
            mal.setPerfect((int)Math.round(perfectscore));
            mal.setIsperfect((int)Math.round(isperfectscore));
            mal.setViewCounts((int)Math.round(viewscore));
            malist.add(mal);
        }
        musicArticleservice.saveOrUpdateBatch(malist);
    }

}
